# This Python file uses the following encoding: utf-8
############################################################################
# Copyright (c) 2025 Li Auto Inc. and its affiliates
# Licensed under the Apache License, Version 2.0(the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
############################################################################
import os
import json
from typing import Dict
from trace_data import IrqData, RunnableData
from trace_util import TraceInfoUtils
from trace_parser import VersionNumber

from perfetto_trace_pb2 import Trace, TrackEvent, TrackDescriptor, TracePacket


class TraceObject():

    def __init__(self):
        self.trace = Trace()
        self.packet_name_iid_dict: Dict[str, int] = {}  # 存储runnable name和iid的映射关系
        self.first_packet = None

    def dump_file(self, output_file):
        with open(output_file, "wb") as f:
            f.write(self.trace.SerializeToString())
        print(f"[INFO] Pftrace data has been dumped to '{output_file}'.")

    def add_packet(self, ts=None):
        packet = self.trace.packet.add()
        if ts is not None:
            packet.timestamp = ts
        return packet

    def add_track_event(self, iid=None, name=None, ts=None, track=None, trusted_sequence_id=0):
        packet = self.add_packet(ts=ts)
        if iid is not None:
            packet.track_event.name_iid = iid
        if name is not None:
            packet.track_event.name = name
        if track is not None:
            packet.track_event.track_uuid = track
        packet.trusted_packet_sequence_id = trusted_sequence_id
        return packet

    def add_track_descriptor(self, uuid, name, parent=None):
        packet = self.add_packet()
        track_descriptor = packet.track_descriptor
        track_descriptor.child_ordering = TrackDescriptor.ChildTracksOrdering.LEXICOGRAPHIC
        if uuid is not None:
            track_descriptor.uuid = uuid
        if name is not None:
            track_descriptor.name = name
        if parent is not None:
            track_descriptor.parent_uuid = parent
        return packet

    def add_iid_packet(self, name, ts, track, trusted_sequence_id=0, event_type=TrackEvent.Type.TYPE_SLICE_BEGIN):
        iid_exist: bool = True
        iid = self.packet_name_iid_dict.get(name)
        if iid is None:
            iid_exist = False
            iid = self.packet_name_iid_dict[name] = len(self.packet_name_iid_dict) + 1
        # (1)创建packet
        packet = self.add_track_event(iid=iid,
                                      name=None,
                                      ts=ts,
                                      track=track,
                                      trusted_sequence_id=trusted_sequence_id)
        packet.track_event.type = event_type
        # (2)是否为first_packet
        if self.first_packet is None:
            packet.previous_packet_dropped = True
            packet.first_packet_on_sequence = True
            packet.sequence_flags = TracePacket.SequenceFlags.SEQ_INCREMENTAL_STATE_CLEARED \
                | TracePacket.SequenceFlags.SEQ_NEEDS_INCREMENTAL_STATE
            self.first_packet = packet
        else:
            packet.sequence_flags = TracePacket.SequenceFlags.SEQ_NEEDS_INCREMENTAL_STATE
        # (3)更新first packet的字符串
        if iid_exist is False:
            event_name = self.first_packet.interned_data.event_names.add()
            event_name.iid = iid
            event_name.name = name
        return packet

    def add_track_event_slice_begin(self, name, ts, track=None, trusted_sequence_id=0):
        return self.add_iid_packet(name, ts, track, trusted_sequence_id, TrackEvent.Type.TYPE_SLICE_BEGIN)

    def add_track_event_slice_end(self, ts, track=None, trusted_sequence_id=0):
        p = self.add_track_event(None, None, ts, track, trusted_sequence_id)
        p.track_event.type = TrackEvent.Type.TYPE_SLICE_END
        return p

    def add_instant_event(self, name, ts, track=None, trusted_sequence_id=0):
        return self.add_iid_packet(name, ts, track, trusted_sequence_id, TrackEvent.Type.TYPE_INSTANT)


class PftraceProducer:
    PREEMPTED = "PREEMPTED"

    def __init__(self) -> None:
        self.trace = TraceObject()
        self.trusted_sequence_id = 3903809
        self.uuid_dict: Dict[str, int] = {}
        self.max_uuid: int = 0  # 当前已有uuid的最大值
        self.conf_dict = {}  # 所有设备的task和runanble的字典信息
        # 所有设备的function信息，需要在解析数据时获取{device_name: {core_id: {func_id: (func_iid, name)}}}
        self.func_dict = {}

        self.max_event_chain_id: int = 0  # 当前已有事件链id的最大值
        # {idx: "item_cnt": 事件链长度, "chain_list": [{"packet_list_list": 就绪的对象列表的列表, 
        # "last_end_time": 上一个对象的结束时间, "first_start_time": 首个对象的开始时间}]}
        self.chain_info_dict: dict = {}
        self.chain_obj_dict: dict = {}  # 存放事件链中每个对象的信息
        self.chain_statistics_dict: dict = {}   # 存放事件链的统计信息
        self.chain_parallel_flag = True   # 是否并行解析事件链

        self.start_time_dict: Dict[str, int] = {}  # {core名称: core内第一条trace报文的时间戳}
        self.core_runn_func_stack: Dict[str, list] = {} # {core名称: [当前正在运行的runnable/function信息]}
        self.core_isr_stack: Dict[str, list] = {}   # {core名称: [当前正在运行的isr信息]}

    def is_uuid_valid(self, name):
        return name in self.uuid_dict

    def get_valid_uuid(self, name):
        if not self.is_uuid_valid(name):
            self.max_uuid += 1
            self.uuid_dict[name] = self.max_uuid
        return self.uuid_dict[name]

    def get_chain_obj_name(self, str_list: list):
        return "".join(str_list)

    def update_flow_id(self, chain_obj_key, time_stamp):
        """事件链对象结束时，判断该事件链对象是否就绪，即是否能添加到事件链list中"""
        # 非事件链对象
        if chain_obj_key not in self.chain_obj_dict:
            return
        chain_obj = self.chain_obj_dict[chain_obj_key]
        if chain_obj["packet_list"] == []:
            return

        for index1, index2 in chain_obj["idx_list"]:
            chain_packet_obj = self.chain_info_dict[index1]
            chain_list = chain_packet_obj["chain_list"]
            # 碰到事件链的起点，开始一条新的事件链
            if index2 == 0:
                # 事件链的首个元素的开始事件没有抓取到
                if chain_obj["start_time"] == -1:
                    continue
                # 若不并行解析，则只能同时解析每种事件链各1条
                if (self.chain_parallel_flag is False) and (len(chain_list) > 0):
                    continue
                chain_list.append({
                    "packet_list_list": [None] * chain_packet_obj["item_cnt"],
                    "last_end_time": time_stamp,
                })
                chain_list[-1]["packet_list_list"][0] = chain_obj["packet_list"]
                chain_list[-1]["first_start_time"] = chain_obj["start_time"]
            # 非起点对象，优先放到最前面的事件链中
            else:
                for i in range(len(chain_list)):
                    chain_info = chain_list[i]
                    # 位置被占
                    if chain_info["packet_list_list"][index2] is not None:
                        continue
                    # 前一个对象未就绪
                    if chain_info["packet_list_list"][index2 - 1] is None:
                        continue
                    # 事件对象就绪，放入就绪队列，非起点对象可以放入同种事件链的多条链中
                    if chain_info["last_end_time"] < chain_obj["start_time"]:
                        chain_info["packet_list_list"][index2] = chain_obj["packet_list"]
                        chain_info["last_end_time"] = time_stamp
                # 就绪队列已满，设置flow_ids
                while (len(chain_list) > 0) and (chain_list[0]["packet_list_list"][-1] is not None):
                    self.max_event_chain_id += 1
                    for packet_list in chain_list[0]["packet_list_list"]:
                        for packet in packet_list:
                            packet.track_event.flow_ids.append(self.max_event_chain_id)
                    chain_statistics_info = self.chain_statistics_dict[index1]
                    first_start_time = chain_list[0]["first_start_time"]
                    cur_time = chain_list[0]["last_end_time"] - first_start_time
                    chain_statistics_info["time_list"].append(cur_time)
                    chain_statistics_info["total_time"] += cur_time
                    chain_statistics_info["total_cnt"] += 1
                    if cur_time > chain_statistics_info["max_time"]:
                        chain_statistics_info["max_time"] = cur_time
                        chain_statistics_info["max_timestamp"] = first_start_time
                    if (chain_statistics_info["min_time"] == -1) or (cur_time < chain_statistics_info["min_time"]):
                        chain_statistics_info["min_time"] = cur_time
                        chain_statistics_info["min_timestamp"] = first_start_time
                    del chain_list[0]
            self.chain_info_dict[index1]["chain_list"] = chain_list
        chain_obj["packet_list"] = []
        self.chain_obj_dict[chain_obj_key] = chain_obj

    def get_core_name(self, device_name, core_id):
        return f"{device_name}_Core{core_id}"

    def get_irq_name(self, device_name, irq_id):
        if device_name not in self.conf_dict:
            return ""
        irq_dict = self.conf_dict[device_name]["ISR"]
        irq_name = irq_dict[irq_id].name
        return f"{device_name}_{irq_name}"

    def get_task_name(self, device_name, task_id):
        if device_name not in self.conf_dict:
            return ""
        task_dict = self.conf_dict[device_name]["Task"]
        task_name = task_dict[task_id].name
        return f"{device_name}_{task_name}"

    def get_runnable_name(self, device_name, core_id, runnable_id):
        if device_name not in self.conf_dict:
            return ""
        runnable_dict = self.conf_dict[device_name]["Runnable"]
        runnable_name = runnable_dict.setdefault(runnable_id, RunnableData(name=f"runnable_{runnable_id}"))
        return f"{device_name}_Core{core_id}_{runnable_name}"

    def get_function_name(self, device_name, core_id, name):
        return f"{device_name}_Core{core_id}_Function_{name}"

    def get_obj_name(self, device_name, core_id, obj_name):
        return f"{device_name}_Core{core_id}_{obj_name}"

    def add_track_tree(self, device_name, core_id, obj_id, obj_type="task"):
        # 构建 device-core-task/irq 层级结构
        device_uuid = None
        if not self.is_uuid_valid(device_name):
            device_uuid = self.get_valid_uuid(device_name)
            self.trace.add_track_descriptor(device_uuid, device_name)
        else:
            device_uuid = self.get_valid_uuid(device_name)

        core_name = self.get_core_name(device_name, core_id)
        core_uuid = None
        if not self.is_uuid_valid(core_name):
            core_uuid = self.get_valid_uuid(core_name)
            self.trace.add_track_descriptor(core_uuid, f"Core{core_id}", device_uuid)
        else:
            core_uuid = self.get_valid_uuid(core_name)

        if obj_type == "task":
            obj_name = self.get_task_name(device_name, obj_id)
        elif obj_type == "irq":
            obj_name = self.get_irq_name(device_name, obj_id)
        elif obj_type == "function":
            obj_name = self.get_function_name(device_name, core_id, obj_id)
        else:
            obj_name = self.get_obj_name(device_name, core_id, obj_type)

        if not self.is_uuid_valid(obj_name):
            obj_uuid = self.get_valid_uuid(obj_name)
            self.trace.add_track_descriptor(obj_uuid, obj_name, core_uuid)
            return obj_uuid
        return self.get_valid_uuid(obj_name)

    def update_start_time(self, device_name, core_id, time_stamp):
        core_name = self.get_core_name(device_name, core_id)
        if core_name not in self.start_time_dict:
            self.start_time_dict[core_name] = int(time_stamp)

    def parse_irq_entry(self, device_name, event_len, payload_list, trace_data_parser):
        result = trace_data_parser.parse_irq_entry(event_len, payload_list)
        if not result:
            return
        core_id, pid, time_stamp, irq_id, ori_irq_id, _ = result
        if trace_data_parser.VERSION_NO == VersionNumber.VCOS_200.value:
            self.conf_dict[device_name]["ISR"][irq_id] = IrqData(name=f"ISR_{ori_irq_id}_core{core_id}")
        # 构建 device-core-irq 层级结构
        self.add_track_tree(device_name, core_id, irq_id, "irq")
        irq_name = self.get_irq_name(device_name, irq_id)
        irq_uuid = self.get_valid_uuid(irq_name)
        time_stamp = int(time_stamp)
        # ISR事件链节点
        packet = self.trace.add_track_event_slice_begin(irq_name, time_stamp, irq_uuid, self.trusted_sequence_id)
        chain_obj_key = self.get_chain_obj_name(["irq", device_name, self.conf_dict[device_name]["ISR"][irq_id].name])
        if chain_obj_key in self.chain_obj_dict:
            self.chain_obj_dict[chain_obj_key]["start_time"] = time_stamp
            self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)

        # 给core增加isr slice
        core_name = self.get_core_name(device_name, core_id)
        core_uuid = self.get_valid_uuid(core_name)
        self.trace.add_track_event_slice_end(time_stamp, core_uuid, self.trusted_sequence_id)
        self.trace.add_track_event_slice_begin(irq_name, time_stamp, core_uuid, self.trusted_sequence_id)

        # ISR嵌套
        if len(self.core_isr_stack.setdefault(core_name, [])) > 0:
            _, preempted_irq_uuid, _ = self.core_isr_stack[core_name][-1]
            # 抢占ISR上的slice，无论其是ISR还是ISR里的function
            self.trace.add_track_event_slice_end(time_stamp, preempted_irq_uuid, self.trusted_sequence_id)
            self.trace.add_track_event_slice_begin(
                self.PREEMPTED, time_stamp, preempted_irq_uuid, self.trusted_sequence_id
            )
        # 无ISR嵌套
        elif len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
            _, _, obj_parent_uuid, _ = self.core_runn_func_stack[core_name][-1]
            task_name = self.get_task_name(device_name, pid)
            task_uuid = self.get_valid_uuid(task_name)
            # 抢占task内的runnable/function
            if task_uuid == obj_parent_uuid:
                self.trace.add_track_event_slice_end(time_stamp, task_uuid, self.trusted_sequence_id)
                self.trace.add_track_event_slice_begin(self.PREEMPTED, time_stamp, task_uuid, self.trusted_sequence_id)
        self.core_isr_stack.setdefault(core_name, []).append((irq_name, irq_uuid, irq_id))

    def parse_irq_exit(self, device_name, event_len, payload_list, trace_data_parser):
        result = trace_data_parser.parse_irq_exit(event_len, payload_list)
        if not result:
            return
        core_id, pid, time_stamp, irq_id, ori_irq_id = result
        if trace_data_parser.VERSION_NO == VersionNumber.VCOS_200.value:
            self.conf_dict[device_name]["ISR"][irq_id] = IrqData(name=f"ISR_{ori_irq_id}_core{core_id}")
        self.update_start_time(device_name, core_id, -1)
        # 构建 device-core-irq 层级结构
        self.add_track_tree(device_name, core_id, irq_id, "irq")
        irq_name = self.get_irq_name(device_name, irq_id)
        irq_uuid = self.get_valid_uuid(irq_name)
        time_stamp = int(time_stamp)
        self.trace.add_track_event_slice_end(time_stamp, irq_uuid, self.trusted_sequence_id)

        # 给core增加isr slice
        cur_task_name = self.get_task_name(device_name, pid)
        core_name = self.get_core_name(device_name, core_id)
        core_uuid = self.get_valid_uuid(core_name)
        self.trace.add_track_event_slice_end(time_stamp, core_uuid, self.trusted_sequence_id)

        # NOTE ISR结束后，若其中的function没有发end事件，也要退出
        while (len(self.core_runn_func_stack.setdefault(core_name, [])) > 0):
            type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
            if obj_parent_uuid == irq_uuid:
                self.core_runn_func_stack[core_name].pop()
                # 结束ISR track的function
                self.trace.add_track_event_slice_end(time_stamp, irq_uuid, self.trusted_sequence_id)
                # 结束function track的function
                if type_text == "function_in_irq":
                    func_uuid = self.add_track_tree(device_name, core_id, obj_name, "function")
                    self.trace.add_track_event_slice_end(time_stamp, func_uuid, self.trusted_sequence_id)
            else:
                break
        # 当前ISR退出后，仍有ISR运行
        if len(self.core_isr_stack.setdefault(core_name, [])) > 1:
            preempted_irq_name, preempted_irq_uuid, _ = self.core_isr_stack[core_name][-2]
            # 恢复core isr slice
            self.trace.add_track_event_slice_begin(preempted_irq_name, time_stamp, core_uuid, self.trusted_sequence_id)
            rebegin_name = preempted_irq_name
            type_text = "irq"
            if len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
                type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
                # 恢复ISR里的function
                if obj_parent_uuid == preempted_irq_uuid:
                    rebegin_name = obj_name
            # 恢复ISR或ISR里的function
            self.trace.add_track_event_slice_end(time_stamp, preempted_irq_uuid, self.trusted_sequence_id)
            packet = self.trace.add_track_event_slice_begin(
                rebegin_name, time_stamp, preempted_irq_uuid, self.trusted_sequence_id
            )
            if rebegin_name != preempted_irq_name:
                # ISR内只可能有function
                chain_obj_key = self.get_chain_obj_name(
                    [type_text, device_name, self.conf_dict[device_name]["ISR"][obj_parnet_id].name, obj_name]
                )
            else:
                # ISR事件链节点
                chain_obj_key = self.get_chain_obj_name([type_text, device_name, preempted_irq_name])
            if chain_obj_key in self.chain_obj_dict:
                self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
        # 当前ISR退出后，无ISR运行
        else:
            # 恢复core task slice
            packet = self.trace.add_track_event_slice_begin(
                cur_task_name, time_stamp, core_uuid, self.trusted_sequence_id
            )
            # task事件链节点
            chain_obj_key = self.get_chain_obj_name(
                ["task", device_name, self.conf_dict[device_name]["Task"][pid].name]
            )
            if chain_obj_key in self.chain_obj_dict:
                self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
            # 恢复
            if len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
                type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
                task_uuid = self.get_valid_uuid(cur_task_name)
                # 恢复task里的runnable/function
                if task_uuid == obj_parent_uuid:
                    self.trace.add_track_event_slice_end(time_stamp, task_uuid, self.trusted_sequence_id)
                    packet = self.trace.add_track_event_slice_begin(
                        obj_name, time_stamp, task_uuid, self.trusted_sequence_id
                    )
                    chain_obj_key = self.get_chain_obj_name(
                        [type_text, device_name, self.conf_dict[device_name]["Task"][obj_parnet_id].name, obj_name]
                    )
                    if chain_obj_key in self.chain_obj_dict:
                        self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
        if len(self.core_isr_stack.setdefault(core_name, [])) > 0:
            self.core_isr_stack[core_name].pop()

        # ISR事件链节点
        chain_obj_key = self.get_chain_obj_name(["irq", device_name, self.conf_dict[device_name]["ISR"][irq_id].name])
        self.update_flow_id(chain_obj_key, time_stamp)

    def parse_task_switch(self, device_name, event_len, payload_list, trace_data_parser):
        """上个task切出,下个task切入"""
        result = trace_data_parser.parse_sched_switch(event_len, payload_list)
        if not result:
            return
        next_prioirty, core_id, next_task_id, time_stamp, prev_task_id, prev_state = result
        self.update_start_time(device_name, core_id, time_stamp)
        # 1) 构建 device-core-task 层级结构
        self.add_track_tree(device_name, core_id, prev_task_id, "task")
        self.add_track_tree(device_name, core_id, next_task_id, "task")
        # 2) 给core增加task slice
        prev_task_name = self.get_task_name(device_name, prev_task_id)
        next_task_name = self.get_task_name(device_name, next_task_id)
        time_stamp = int(time_stamp)
        core_name = self.get_core_name(device_name, core_id)
        core_uuid = self.get_valid_uuid(core_name)
        if core_name in self.start_time_dict and self.start_time_dict[core_name] != -1:
            self.trace.add_track_event_slice_begin(prev_task_name, self.start_time_dict[core_name], core_uuid,
                                                   self.trusted_sequence_id)
            self.start_time_dict[core_name] = -1
        self.trace.add_track_event_slice_end(time_stamp, core_uuid, self.trusted_sequence_id)
        # next_task事件链节点
        packet = self.trace.add_track_event_slice_begin(next_task_name, time_stamp, core_uuid, self.trusted_sequence_id)
        next_task_data = self.conf_dict[device_name]["Task"][next_task_id]
        chain_obj_key = self.get_chain_obj_name(["task", device_name, next_task_data.name])
        if chain_obj_key in self.chain_obj_dict:
            if next_task_data.ready_state is True:
                self.chain_obj_dict[chain_obj_key]["start_time"] = time_stamp
            self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
        next_task_data.ready_state = False

        prev_task_uuid = self.get_valid_uuid(prev_task_name)
        next_task_uuid = self.get_valid_uuid(next_task_name)
        # prev_task结束
        if trace_data_parser.is_task_blocked(prev_state) is True:
            # NOTE 实际上task结束执行后没有立刻ready，只是方便在无wake up事件时，分析task状态
            prev_task_data = self.conf_dict[device_name]["Task"][prev_task_id]
            prev_task_data.ready_state = True
            # prev_task事件链节点
            chain_obj_key = self.get_chain_obj_name(["task", device_name, prev_task_data.name])
            self.update_flow_id(chain_obj_key, time_stamp)
            # NOTE prev_task结束时，若其中的runnable/function没有发end事件，也要退出
            while len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
                type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
                if prev_task_uuid == obj_parent_uuid:
                    self.core_runn_func_stack[core_name].pop()
                    # 结束task track的runnable/function
                    self.trace.add_track_event_slice_end(time_stamp, prev_task_uuid, self.trusted_sequence_id)
                    # 结束function track的function
                    if type_text == "function_in_task":
                        func_uuid = self.add_track_tree(device_name, core_id, obj_name, "function")
                        self.trace.add_track_event_slice_end(time_stamp, func_uuid, self.trusted_sequence_id)
                else:
                    break
        # 抢占/恢复
        if len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
            type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
            # 抢占prev_task的runnable/function
            if prev_task_uuid == obj_parent_uuid:
                self.trace.add_track_event_slice_end(time_stamp, prev_task_uuid, self.trusted_sequence_id)
                self.trace.add_track_event_slice_begin(
                    self.PREEMPTED, time_stamp, prev_task_uuid, self.trusted_sequence_id
                )

            # 恢复next_task里的runnable/function
            if next_task_uuid == obj_parent_uuid:
                self.trace.add_track_event_slice_end(time_stamp, next_task_uuid, self.trusted_sequence_id)
                packet = self.trace.add_track_event_slice_begin(
                    obj_name, time_stamp, next_task_uuid, self.trusted_sequence_id
                )
                # 记事件链节点
                chain_obj_key = self.get_chain_obj_name(
                    [type_text, device_name, self.conf_dict[device_name]["Task"][obj_parnet_id].name, obj_name]
                )
                if chain_obj_key in self.chain_obj_dict:
                    self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
            # 继续抢占其他task里的runnable/function
            else:
                pass

    def parse_task_suspend(self, device_name, event_len, payload_list, trace_data_parser):
        """task切出"""
        result = trace_data_parser.parse_suspend(event_len, payload_list)
        if not result:
            return
        core_id, prev_task_id, time_stamp, prev_state = result
        self.update_start_time(device_name, core_id, time_stamp)
        # 1) 构建 device-core-task 层级结构
        self.add_track_tree(device_name, core_id, prev_task_id, "task")
        # 2) 给core增加task slice
        prev_task_name = self.get_task_name(device_name, prev_task_id)
        time_stamp = int(time_stamp)
        core_name = self.get_core_name(device_name, core_id)
        core_uuid = self.get_valid_uuid(core_name)
        # task是否已被ISR抢占
        if len(self.core_isr_stack.setdefault(core_name, [])) == 0:
            self.trace.add_track_event_slice_end(time_stamp, core_uuid, self.trusted_sequence_id)

        # prev_task结束
        prev_task_uuid = self.get_valid_uuid(prev_task_name)
        if trace_data_parser.is_task_blocked(prev_state) is True:
            # NOTE 实际上task结束执行后没有立刻ready，只是方便在无wake up事件时，分析task状态
            prev_task_data = self.conf_dict[device_name]["Task"][prev_task_id]
            prev_task_data.ready_state = True
            # prev_task事件链节点
            chain_obj_key = self.get_chain_obj_name(["task", device_name, prev_task_data.name])
            self.update_flow_id(chain_obj_key, time_stamp)
            # NOTE prev_task结束时，若其中的runnable/function没有发end事件，也要退出
            while len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
                type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
                if prev_task_uuid == obj_parent_uuid:
                    self.core_runn_func_stack[core_name].pop()
                    # 结束task track的runnable/function
                    self.trace.add_track_event_slice_end(time_stamp, prev_task_uuid, self.trusted_sequence_id)
                    # 结束function track的function
                    if type_text == "function_in_task":
                        func_uuid = self.add_track_tree(device_name, core_id, obj_name, "function")
                        self.trace.add_track_event_slice_end(time_stamp, func_uuid, self.trusted_sequence_id)
                else:
                    break
        # 抢占
        if (len(self.core_isr_stack.setdefault(core_name, [])) == 0) and \
            len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
            type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
            # 抢占prev_task的runnable/function
            if prev_task_uuid == obj_parent_uuid:
                self.trace.add_track_event_slice_end(time_stamp, prev_task_uuid, self.trusted_sequence_id)
                self.trace.add_track_event_slice_begin(
                    self.PREEMPTED, time_stamp, prev_task_uuid, self.trusted_sequence_id
                )

    def parse_task_resume(self, device_name, event_len, payload_list, trace_data_parser):
        """task切入"""
        result = trace_data_parser.parse_resume(event_len, payload_list)
        if not result:
            return
        _, core_id, next_task_id, time_stamp = result
        # 1) 构建 device-core-task 层级结构
        self.add_track_tree(device_name, core_id, next_task_id, "task")
        # 2) 给core增加task slice
        next_task_name = self.get_task_name(device_name, next_task_id)
        time_stamp = int(time_stamp)
        core_name = self.get_core_name(device_name, core_id)
        core_uuid = self.get_valid_uuid(core_name)
        # ISR里task不运行
        packet = None
        if len(self.core_isr_stack.setdefault(core_name, [])) == 0:
            packet = self.trace.add_track_event_slice_begin(next_task_name, time_stamp, core_uuid, self.trusted_sequence_id)
        # next_task事件链节点
        next_task_data = self.conf_dict[device_name]["Task"][next_task_id]
        chain_obj_key = self.get_chain_obj_name(["task", device_name, next_task_data.name])
        if chain_obj_key in self.chain_obj_dict:
            if next_task_data.ready_state is True:
                self.chain_obj_dict[chain_obj_key]["start_time"] = time_stamp
            if packet is not None:
                self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
        next_task_data.ready_state = False

        # 恢复
        next_task_uuid = self.get_valid_uuid(next_task_name)
        if (len(self.core_isr_stack.setdefault(core_name, [])) == 0) and \
            len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
            type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
            # 恢复next_task里的runnable/function
            if next_task_uuid == obj_parent_uuid:
                self.trace.add_track_event_slice_end(time_stamp, next_task_uuid, self.trusted_sequence_id)
                packet = self.trace.add_track_event_slice_begin(
                    obj_name, time_stamp, next_task_uuid, self.trusted_sequence_id
                )
                # 记事件链节点
                chain_obj_key = self.get_chain_obj_name(
                    [type_text, device_name, self.conf_dict[device_name]["Task"][obj_parnet_id].name, obj_name]
                )
                if chain_obj_key in self.chain_obj_dict:
                    self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
            # 继续抢占其他task里的runnable/function
            else:
                pass

    def parse_runnable_start(self, device_name, event_len, payload_list, trace_data_parser):
        # 1）解析payload报文
        result = trace_data_parser.parse_runnable_s(event_len, payload_list)
        if not result:
            return
        priority, core_id, task_id, time_stamp, runnable_id = result
        self.update_start_time(device_name, core_id, time_stamp)
        # 2) 构建 device-core-task 层级结构
        self.add_track_tree(device_name, core_id, task_id, "task")

        # 3）增加runnable信息packet
        core_name = self.get_core_name(device_name, core_id)
        task_name = self.get_task_name(device_name, task_id)
        task_uuid = self.get_valid_uuid(task_name)
        time_stamp = int(time_stamp)
        # NOTE runnable开始时，如果同一task里的上一个runnable没发end信号，也要结束
        if len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
            type_text, obj_name, obj_parent_uuid, obj_parnet_id = self.core_runn_func_stack[core_name][-1]
            if (obj_parent_uuid == task_uuid) and (type_text == "runnable"):
                self.core_runn_func_stack[core_name].pop()
                self.trace.add_track_event_slice_end(time_stamp, task_uuid, self.trusted_sequence_id)
        runnable_name = self.conf_dict[device_name]["Runnable"][runnable_id].name
        packet = self.trace.add_track_event_slice_begin(runnable_name, time_stamp, task_uuid, self.trusted_sequence_id)
        # 4）记录runnable开始的时间和packet
        chain_obj_key = self.get_chain_obj_name(
            ["runnable", device_name, self.conf_dict[device_name]["Task"][task_id].name, runnable_name]
        )
        if chain_obj_key in self.chain_obj_dict:
            self.chain_obj_dict[chain_obj_key]["start_time"] = time_stamp
            self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)

        # 5）更新core的运行runnable
        self.core_runn_func_stack.setdefault(core_name, []).append(("runnable", runnable_name, task_uuid, task_id))

    def parse_runnable_end(self, device_name, event_len, payload_list, trace_data_parser):
        result = trace_data_parser.parse_runnable_s(event_len, payload_list)
        if not result:
            return
        priority, core_id, task_id, time_stamp, runnable_id = result
        self.update_start_time(device_name, core_id, time_stamp)
        # 1) 构建 device-core-task 层级结构
        self.add_track_tree(device_name, core_id, task_id, "task")

        # 2）增加runnable信息
        task_name = self.get_task_name(device_name, task_id)
        task_uuid = self.get_valid_uuid(task_name)
        time_stamp = int(time_stamp)

        # 3）runnable结束时，判断是否要加入事件链中
        runnable_name = self.conf_dict[device_name]["Runnable"][runnable_id].name
        chain_obj_key = self.get_chain_obj_name(
            ["runnable", device_name, self.conf_dict[device_name]["Task"][task_id].name, runnable_name]
        )
        self.update_flow_id(chain_obj_key, time_stamp)
        # track_event_slice_end在实现上等同于flow_event_slice_end
        self.trace.add_track_event_slice_end(time_stamp, task_uuid, self.trusted_sequence_id)

        # 4）更新core的运行runnable
        core_name = self.get_core_name(device_name, core_id)
        if len(self.core_runn_func_stack.setdefault(core_name, [])) > 0:
            self.core_runn_func_stack[core_name].pop()

    def parse_event(self, device_name, event_len, payload_list, trace_data_parser):
        result = trace_data_parser.parse_event_start(event_len, payload_list)
        if not result:
            return
        core_id, task_id, time_stamp, event_id = result
        self.update_start_time(device_name, core_id, time_stamp)

        # 1) 构建 device-core-task 层级结构
        self.add_track_tree(device_name, core_id, task_id, "task")

        # 2）增加event信息
        task_name = self.get_task_name(device_name, task_id)
        task_uuid = self.get_valid_uuid(task_name)
        event_name = f"event_{event_id}" if isinstance(event_id, int) else event_id
        time_stamp = int(time_stamp)

        self.trace.add_instant_event(event_name, time_stamp, task_uuid, self.trusted_sequence_id)

    def parse_spinlock(self, device_name, event_len, payload_list, is_get: bool, trace_data_parser):
        # 解析payload报文
        result = trace_data_parser.parse_spinlock(event_len, payload_list)
        if not result:
            return
        core_id, time_stamp, spinlock_id = result
        # 构建device-core-spinlock层级结构
        self.add_track_tree(device_name, core_id, -1, "SPINLOCK")
        spinlock_name = self.get_obj_name(device_name, core_id, "SPINLOCK")
        spinlock_uuid = self.get_valid_uuid(spinlock_name)
        time_stamp = int(time_stamp)
        if is_get is True:
            self.trace.add_track_event_slice_begin(
                f"{spinlock_id}", time_stamp, spinlock_uuid, self.trusted_sequence_id
            )
        else:
            self.trace.add_track_event_slice_end(time_stamp, spinlock_uuid, self.trusted_sequence_id)

    def parse_resource(self, device_name, event_len, payload_list, is_get: bool, trace_data_parser):
        # 解析payload报文
        result = trace_data_parser.parse_resource(event_len, payload_list)
        if not result:
            return
        core_id, time_stamp, resource_id = result
        # 构建device-core-resource层级结构
        self.add_track_tree(device_name, core_id, -1, "RESOURCE")
        resource_name = self.get_obj_name(device_name, core_id, "RESOURCE")
        resource_uuid = self.get_valid_uuid(resource_name)
        time_stamp = int(time_stamp)
        if is_get is True:
            self.trace.add_track_event_slice_begin(
                f"{resource_id}", time_stamp, resource_uuid, self.trusted_sequence_id
            )
        else:
            self.trace.add_track_event_slice_end(time_stamp, resource_uuid, self.trusted_sequence_id)

    def parse_interrupt(self, device_name, event_len, payload_list, event_name, is_disable: bool, trace_data_parser):
        result = trace_data_parser.parse_interrupt_s(event_len, payload_list)
        if not result:
            return
        core_id, time_stamp = result

        self.add_track_tree(device_name, core_id, -1, event_name)
        int_name = self.get_obj_name(device_name, core_id, event_name)
        int_uuid = self.get_valid_uuid(int_name)
        time_stamp = int(time_stamp)
        if is_disable is True:
            self.trace.add_track_event_slice_begin(event_name, time_stamp, int_uuid, self.trusted_sequence_id)
        else:
            self.trace.add_track_event_slice_end(time_stamp, int_uuid, self.trusted_sequence_id)

    def parse_function_start(self, device_name, event_len, payload_list, trace_data_parser):
        result = trace_data_parser.parse_function_s(event_len, payload_list, True)
        if not result:
            return
        core_id, task_id, time_stamp, func_id, events = result
        self.add_track_tree(device_name, core_id, task_id, "task")
        time_stamp = int(time_stamp)
        core_name = self.get_core_name(device_name, core_id)
        # (1)function track
        func_uuid = self.add_track_tree(device_name, core_id, events, "function")
        self.func_dict.setdefault(device_name, {}).setdefault(core_id, {})[func_id] = (func_uuid, events)
        packet = self.trace.add_track_event_slice_begin(events, time_stamp, func_uuid, self.trusted_sequence_id)
        # core里的function
        chain_obj_key = self.get_chain_obj_name(["function_in_core", device_name, str(core_id), events])
        if chain_obj_key in self.chain_obj_dict:
            self.chain_obj_dict[chain_obj_key]["start_time"] = time_stamp
            self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
        # (2)线程内的function
        # ISR里的function
        if len(self.core_isr_stack.setdefault(core_name, [])) > 0:
            _, irq_uuid, irq_id = self.core_isr_stack[core_name][-1]
            chain_obj_key = self.get_chain_obj_name(
                ["function_in_irq", device_name, self.conf_dict[device_name]["ISR"][irq_id].name, events]
            )
            if chain_obj_key in self.chain_obj_dict:
                packet = self.trace.add_track_event_slice_begin(events, time_stamp, irq_uuid, self.trusted_sequence_id)
                # 记录当前运行的function
                self.chain_obj_dict[chain_obj_key]["start_time"] = time_stamp
                self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
                self.core_runn_func_stack.setdefault(core_name, []).append(
                    ("function_in_irq", events, irq_uuid, irq_id))
        # task里的function
        else:
            chain_obj_key = self.get_chain_obj_name(
                ["function_in_task", device_name, self.conf_dict[device_name]["Task"][task_id].name, events]
            )
            if chain_obj_key in self.chain_obj_dict:
                task_name = self.get_task_name(device_name, task_id)
                task_uuid = self.get_valid_uuid(task_name)
                packet = self.trace.add_track_event_slice_begin(events, time_stamp, task_uuid, self.trusted_sequence_id)
                # 记录当前运行的function
                self.chain_obj_dict[chain_obj_key]["start_time"] = time_stamp
                self.chain_obj_dict[chain_obj_key]["packet_list"].append(packet)
                self.core_runn_func_stack.setdefault(core_name, []).append(
                    ("function_in_task", events, task_uuid, task_id))

    def parse_function_end(self, device_name, event_len, payload_list, trace_data_parser):
        result = trace_data_parser.parse_function_s(event_len, payload_list, False)
        if not result:
            return
        core_id, task_id, time_stamp, func_id, _ = result
        self.add_track_tree(device_name, core_id, task_id, "task")
        time_stamp = int(time_stamp)
        core_name = self.get_core_name(device_name, core_id)
        # (1)function track
        if func_id in self.func_dict.setdefault(device_name, {}).setdefault(core_id, {}):
            func_uuid, function_name = self.func_dict[device_name][core_id][func_id]
            self.trace.add_track_event_slice_end(time_stamp, func_uuid, self.trusted_sequence_id)
            # core里的function
            chain_obj_key = self.get_chain_obj_name(["function_in_core", device_name, f"{core_id}", function_name])
            self.update_flow_id(chain_obj_key, time_stamp)
        # (2)线程内的function
        # function出栈
        if len(self.core_runn_func_stack.setdefault(core_name, [])) == 0:
            return
        type_text, func_name_in_stack, func_parent_uuid, _ = self.core_runn_func_stack[core_name][-1]
        # ISR里的function
        if len(self.core_isr_stack.setdefault(core_name, [])) > 0:
            _, irq_uuid, irq_id = self.core_isr_stack[core_name][-1]
            if (func_name_in_stack == function_name) and (func_parent_uuid == irq_uuid):
                chain_obj_key = self.get_chain_obj_name(
                    ["function_in_irq", device_name, self.conf_dict[device_name]["ISR"][irq_id].name, function_name]
                )
                self.update_flow_id(chain_obj_key, time_stamp)
                # function结束
                self.trace.add_track_event_slice_end(time_stamp, func_parent_uuid, self.trusted_sequence_id)
                self.core_runn_func_stack[core_name].pop()
        # task里的function
        else:
            task_name = self.get_task_name(device_name, task_id)
            task_uuid = self.get_valid_uuid(task_name)
            if (type_text == "function_in_task") and (func_name_in_stack == function_name) \
                and (func_parent_uuid == task_uuid):
                chain_obj_key = self.get_chain_obj_name(
                    ["function_in_task", device_name, self.conf_dict[device_name]["Task"][task_id].name, function_name]
                )
                self.update_flow_id(chain_obj_key, time_stamp)
                # function结束
                self.trace.add_track_event_slice_end(time_stamp, func_parent_uuid, self.trusted_sequence_id)
                self.core_runn_func_stack[core_name].pop()

    def parse_trace_data(self, trace_data_list, device_data_dict, time_format):
        """trace数据转pftrace"""
        assist_info = {}    # 解析时的辅助字典
        for data_list in trace_data_list:
            device_id = data_list[-1]
            if device_id not in device_data_dict:
                print(f"[ERROR] parse_trace_data(): device id({device_id}) is wrong.")
                continue
            device_data = device_data_dict[device_id]
            device_name = device_data["device_name"]
            data_list = data_list[:-1]
            cur_idx: int = 0
            event_len: int = data_list[cur_idx]
            if event_len > len(data_list):
                print(f"[ERROR] parse_trace_data(): length of the event({event_len}) is wrong.")
                continue
            if "trace_parser" not in assist_info.setdefault(device_id, {}):
                trace_data_parser = TraceInfoUtils.get_trace_parser_from_device_data(device_data)
                if trace_data_parser is None:
                    return
                assist_info[device_id]["trace_parser"] = trace_data_parser
            trace_data_parser = assist_info[device_id]["trace_parser"]
            if "tick_to_ns" not in assist_info.setdefault(device_id, {}):
                if time_format == "local_ns":
                    assist_info[device_id]["tick_to_ns"] = 1
                else:
                    assist_info[device_id]["tick_to_ns"] = 1e9 / device_data["stm_frequency"]
            trace_data_parser.tick_to_ns = assist_info[device_id]["tick_to_ns"]
            if "offset" not in assist_info.setdefault(device_id, {}):
                if time_format == "Beijing":
                    _, _, _, ts = trace_data_parser.parse_payload_common(data_list)
                    if ts == 0:
                        continue
                    assist_info[device_id]["offset"] = device_data["first_utc"] * 1e9 - ts + 8 * 3600 * 1e9
                else:
                    assist_info[device_id]["offset"] = 0
            trace_data_parser.time_offset = assist_info[device_id]["offset"]
            event_type = data_list[cur_idx + 1]
            if event_type == trace_data_parser.FTRACE_SCHED_SWITCH:
                self.parse_task_switch(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_TASK_SUSPEND:
                self.parse_task_suspend(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_TASK_RESUME:
                self.parse_task_resume(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_IRQ_ENTRY:
                self.parse_irq_entry(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_IRQ_EXIT:
                self.parse_irq_exit(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_EVENT_START:
                self.parse_event(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_RUNNABLE_START:
                self.parse_runnable_start(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_RUNNABLE_END:
                self.parse_runnable_end(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_FUNCTION_START:
                self.parse_function_start(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_FUNCTION_END:
                self.parse_function_end(device_name, event_len, data_list, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_GET_SPINLOCK:
                self.parse_spinlock(device_name, event_len, data_list, True, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_RELEASE_SPINLOCK:
                self.parse_spinlock(device_name, event_len, data_list, False, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_GET_RESOURCE:
                self.parse_resource(device_name, event_len, data_list, True, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_RELEASE_RESOURCE:
                self.parse_resource(device_name, event_len, data_list, False, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_DISABLE_ALL_INT:
                self.parse_interrupt(device_name, event_len, data_list, "DisableAllInterrupt", True, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_ENABLE_ALL_INT:
                self.parse_interrupt(device_name, event_len, data_list, "DisableAllInterrupt", False, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_SUSPEND_ALL_INT:
                self.parse_interrupt(device_name, event_len, data_list, "SuspendAllInterrupt", True, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_RESUME_ALL_INT:
                self.parse_interrupt(device_name, event_len, data_list, "SuspendAllInterrupt", False, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_SUSPEND_OS_INT:
                self.parse_interrupt(device_name, event_len, data_list, "SuspendOsInterrupt", True, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_RESUME_OS_INT:
                self.parse_interrupt(device_name, event_len, data_list, "SuspendOsInterrupt", False, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_SUSPEND_KERNEL_INT:
                self.parse_interrupt(device_name, event_len, data_list, "SuspendKernel", True, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_RESUME_KERNEL_INT:
                self.parse_interrupt(device_name, event_len, data_list, "SuspendKernel", False, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_DISABLE_GLOBAL:
                self.parse_interrupt(device_name, event_len, data_list, "DisableGlobal", True, trace_data_parser)
            elif event_type == trace_data_parser.FTRACE_ENABLE_GLOBAL:
                self.parse_interrupt(device_name, event_len, data_list, "DisableGlobal", False, trace_data_parser)
            else:
                continue

    def parse_event_chain_file(self, event_chain_file) -> list:
        """解析事件链配置信息"""
        if os.path.exists(event_chain_file) is False:
            print(f"[WARNING] Event chain config file '{event_chain_file}' does not exist.")
            return True     # 允许不设置事件链
        with open(event_chain_file, "r", encoding="utf-8") as f:
            data = json.load(f)
        if isinstance(data, list) is False:
            print(f"[ERROR] Content of config file {event_chain_file} must be a list.")
            return False

        chain_validation_info = {}
        for index1, event_chain_item in enumerate(data):
            chain_validation_info[index1] = {
                "task": [],
                "irq": [],
                "parent_task": [],
                "parent_irq": []
            }
            # 校验事件链每个节点的格式和内容
            for index2, target_item in enumerate(event_chain_item):
                for key in ["device_name", "node_type"]:
                    if key not in target_item:
                        print(f"[ERROR] The {index2}-th item of the {index1}-th chain is missing '{key}'.")
                        return False
                device_name, node_type = target_item["device_name"], target_item["node_type"]
                if node_type == "task":
                    key_list = sorted(["device_name", "node_type", "task_name"])
                    if sorted(list(target_item.keys())) != key_list:
                        print(f"[ERROR] Keys in the {index2}-th item of the {index1}-th chain must be {key_list}.")
                        return False
                    task_name = target_item["task_name"]
                    if TraceInfoUtils.check_task_name(self.conf_dict, device_name, task_name) is False:
                        return False
                    chain_obj_key = self.get_chain_obj_name([node_type, device_name, task_name])
                    chain_validation_info[index1][node_type].append((device_name, task_name))
                elif node_type == "irq":
                    key_list = sorted(["device_name", "node_type", "irq_name"])
                    if sorted(list(target_item.keys())) != key_list:
                        print(f"[ERROR] Keys in the {index2}-th item of the {index1}-th chain must be {key_list}.")
                        return False
                    irq_name = target_item["irq_name"]
                    if TraceInfoUtils.check_irq_name(self.conf_dict, device_name, irq_name) is False:
                        return False
                    chain_obj_key = self.get_chain_obj_name([node_type, device_name, irq_name])
                    chain_validation_info[index1][node_type].append((device_name, irq_name))
                elif node_type == "runnable":
                    key_list = sorted(["device_name", "node_type", "task_name", "runnable_name"])
                    if sorted(list(target_item.keys())) != key_list:
                        print(f"[ERROR] Keys in the {index2}-th item of the {index1}-th chain must be {key_list}.")
                        return False
                    task_name = target_item["task_name"]
                    if TraceInfoUtils.check_runnable_name(self.conf_dict, device_name, 
                                                          task_name, target_item["runnable_name"]) is False:
                        return False
                    chain_obj_key = self.get_chain_obj_name([
                        node_type, device_name, task_name, target_item["runnable_name"]
                    ])
                    chain_validation_info[index1]["parent_task"].append((device_name, task_name))
                elif node_type == "function_in_task":
                    key_list = sorted(["device_name", "node_type", "task_name", "function_name"])
                    if sorted(list(target_item.keys())) != key_list:
                        print(f"[ERROR] Keys in the {index2}-th item of the {index1}-th chain must be {key_list}.")
                        return False
                    task_name = target_item["task_name"]
                    if TraceInfoUtils.check_task_name(self.conf_dict, device_name, task_name) is False:
                        return False
                    chain_obj_key = self.get_chain_obj_name([
                        node_type, device_name, task_name, target_item["function_name"]
                    ])
                    chain_validation_info[index1]["parent_task"].append((device_name, task_name))
                elif node_type == "function_in_irq":
                    key_list = sorted(["device_name", "node_type", "irq_name", "function_name"])
                    if sorted(list(target_item.keys())) != key_list:
                        print(f"[ERROR] Keys in the {index2}-th item of the {index1}-th chain must be {key_list}.")
                        return False
                    irq_name = target_item["irq_name"]
                    if TraceInfoUtils.check_irq_name(self.conf_dict, device_name, irq_name) is False:
                        return False
                    chain_obj_key = self.get_chain_obj_name([
                        node_type, device_name, irq_name, target_item["function_name"]
                    ])
                    chain_validation_info[index1]["parent_irq"].append((device_name, irq_name))
                elif node_type == "function_in_core":
                    key_list = sorted(["device_name", "node_type", "core_id", "function_name"])
                    if sorted(list(target_item.keys())) != key_list:
                        print(f"[ERROR] Keys in the {index2}-th item of the {index1}-th chain must be {key_list}.")
                        return False
                    chain_obj_key = self.get_chain_obj_name([
                        node_type, device_name, str(target_item["core_id"]), target_item["function_name"]
                    ])
                else:
                    print(f"[ERROR] The 'node_type' of the {index2}-th item in the {index1}-th chain is incorrect.")
                    return False

                if chain_obj_key not in self.chain_obj_dict:
                    self.chain_obj_dict[chain_obj_key] = {
                        "start_time": -1,
                        "idx_list": [(index1, index2)],
                        "packet_list": []
                    }
                else:
                    self.chain_obj_dict[chain_obj_key]["idx_list"].append((index1, index2))
                self.chain_statistics_dict[index1] = {
                    "time_list": [],
                    "total_time": 0,
                    "total_cnt": 0,
                    "max_time": 0,
                    "min_time": -1,
                    "max_timestamp": -1,
                    "min_timestamp": -1
                }
            self.chain_info_dict[index1] = {
                "item_cnt": len(event_chain_item),
                "chain_list": []
            }
        # 校验事件链合理性
        for index, info in chain_validation_info.items():
            for data in info["parent_task"]:
                if data in info["task"]:
                    print(f"[ERROR] In {index}-th chain, there are both runnable/function and task nodes, \
and runnable/function belongs to the task '{data}'.")
                    return False
            for data in info["parent_irq"]:
                if data in info["irq"]:
                    print(f"[ERROR] In {index}-th chain, there are both function and irq nodes, \
and function belongs to the irq '{data}'.")
                    return False
        return True

    def produce_pftrace_file(self, data_dir, event_chain_conf_path, device_list=[], 
                             output_flag=True, time_format="local_tick"):
        """将多个设备的二进制数据文件生成一个统一的pftrace二进制文件"""
        if os.path.isdir(data_dir) is False:
            print(f"[ERROR] data_dir '{data_dir}' does not exist.")
            return False
        # 1) 解析config.json文件
        config_file = os.path.join(data_dir, "config.json")
        self.conf_dict = TraceInfoUtils.parse_config_file(config_file)
        if self.conf_dict is None:
            return False
        # 2) 解析even_chain.json文件
        print(f"[INFO] Parsing event chain config file '{event_chain_conf_path}'...")
        res = self.parse_event_chain_file(event_chain_conf_path)
        if res is False:
            print(f"[ERROR] Failed to parse event chain config file '{event_chain_conf_path}'.")
            return
        else:
            print(f"[INFO] Successfully parsed event chain config file '{event_chain_conf_path}'.")

        # 3) 解析trace数据并构建pftrace文件
        if len(device_list) == 0:
            device_list = list(self.conf_dict)
        device_data_dict = {}
        trace_data_list = []
        for device_name in device_list:
            if device_name not in self.conf_dict:
                print(f"[WARNING] {device_name} is not in config.json.")
                continue
            device_data = self.conf_dict[device_name]
            device_id = device_data["device_id"]
            device_data_dict[device_id] = device_data
            device_data_dict[device_id]["device_name"] = device_name
            trace_data_parser = TraceInfoUtils.get_trace_parser_from_device_data(device_data)
            if trace_data_parser is None:
                return False
            # 获取二进制trace数据帧
            bin_file = os.path.join(data_dir, f"{device_name}_data.bin")
            if os.path.exists(bin_file) is False:
                print(f"[ERROR] '{bin_file}' does not exist.")
                return False
            print(f"[INFO] Parsing trace data file '{bin_file}'...")
            with open(bin_file, "rb") as f:
                bin_data = f.read()
            bin_data = bytearray(bin_data)
            data_list = trace_data_parser.get_trace_data_list(bin_data, bytearray([device_id]))
            trace_data_list += trace_data_parser.sort_trace_data_list(data_list)
        # 二进制报文按时间戳递增排序
        self.parse_trace_data(trace_data_list, device_data_dict, time_format)

        if output_flag is True:
            self.trace.dump_file(os.path.join(data_dir, "trace_output.pb"))
            chain_statistics_res = self.calc_chain_statistics()
            if len(chain_statistics_res) > 0:
                print(f"event_chain_info:\n{'index':<7}{'avg_time(ns)':<20}{'variance(ns)':<25}\
{'max_time(ns)':<20}{'max_timestamp(ns)':<20}{'min_time(ns)':<20}{'min_timestamp(ns)':<20}")
                for idx, info in chain_statistics_res.items():
                    print(f"{idx:<7}{info['avg_time']:<20.3f}{info['variance']:<25.3f}\
{info['max_time']:<20}{info['max_timestamp']:<20}{info['min_time']:<20}{info['min_timestamp']:<20}")
        return True

    def calc_chain_statistics(self):
        """计算事件链的统计信息"""
        ret_dict = {}
        for idx, info in self.chain_statistics_dict.items():
            ret_dict[idx] = {
                "avg_time": info["total_time"] / info["total_cnt"] if info["total_cnt"] > 0 else 0,
                "max_time": info["max_time"],
                "min_time": info["min_time"],
                "max_timestamp": info["max_timestamp"],
                "min_timestamp": info["min_timestamp"]
            }
            time_list = info["time_list"]
            n = len(time_list)
            if n > 0:
                mean = sum(time_list) / n
                ret_dict[idx]["variance"] = sum((x - mean) ** 2 for x in time_list) / n
            else:
                ret_dict[idx]["variance"] = 0
        return ret_dict


class PftraceUtils:
    @classmethod
    def trace_get_chain_statistics(cls, data_dir: str, event_chain_conf_path: str, device_list: list=[]):
        """根据bin文件计算事件链的统计信息"""
        pftrace_producer = PftraceProducer()
        res = pftrace_producer.produce_pftrace_file(data_dir, event_chain_conf_path, device_list, False)
        if res is False:
            return False, None
        ret_dict = pftrace_producer.calc_chain_statistics()
        return True, ret_dict

